import re
from google.appengine.ext import ndb

AIRPLANE = 'airplane'
ROTORCRAFT = 'rotorcraft'
GLIDER = 'glider'
LIGHTER_THAN_AIR = 'lighter-than-air'
SIM = 'simulator'
AIRCRAFT_CATEGORIES = (AIRPLANE, ROTORCRAFT, GLIDER, LIGHTER_THAN_AIR, SIM)

CLASS_L = 'land'
CLASS_S = 'seaplane'
CLASS_A = 'amphibian'
CLASS_D = 'dirigible'
CLASS_G = 'gyrocopter'
CLASS_T = 'tiltwing'
CLASS_NA = 'NA'
AIRCRAFT_CLASSES = (
    CLASS_L, CLASS_S, CLASS_A, CLASS_D, CLASS_G, CLASS_T, CLASS_NA)

ENGINE_P = 'piston'
ENGINE_T = 'turboprop'
ENGINE_J = 'jet'
ENGINE_NA = 'NA'
ENGINE_TYPES = (ENGINE_P, ENGINE_T, ENGINE_J, ENGINE_NA)

NAVAIDS = ('GPS', 'VOR', 'NDB')
RUNWAY_TYPES = ('paved', 'turf')


class Once(ndb.Model):
  pass


class AircraftStat(ndb.Model):
  make_model = ndb.StringProperty()
  hours = ndb.FloatProperty(default=0)


class FlightStats(ndb.Model):
  dual_hours = ndb.FloatProperty(default=0)
  ground_instruction_hours = ndb.FloatProperty(default=0)
  imc_hours = ndb.FloatProperty(default=0)
  night_hours = ndb.FloatProperty(default=0)
  pic_hours = ndb.FloatProperty(default=0)
  sim_imc_hours = ndb.FloatProperty(default=0)
  simulator_hours = ndb.FloatProperty(default=0)
  tailwheel_hours = ndb.FloatProperty(default=0)
  total_hours = ndb.FloatProperty(default=0)
  xc_hours = ndb.FloatProperty(default=0)

  aircraft_stats = ndb.StructuredProperty(AircraftStat, repeated=True)
  num_landings = ndb.IntegerProperty(default=0)
  num_approaches = ndb.IntegerProperty(default=0)
  recent_approaches = ndb.DateTimeProperty(repeated=True)
  total_cost = ndb.FloatProperty(default=0)
  total_num_flights = ndb.IntegerProperty(default=0)

  @property
  def instrument_rating_training_hours(self):
    hours = 0
    hours += self.imc_hours
    hours += self.sim_imc_hours
    if self.simulator_hours > 20:
      hours += 20
    else:
      hours += self.simulator_hours
    hours -= 3  # Private pilot training time doesn't count.
    hours -= 1.1  # Hours in simulator before instrument training don't count.
    return hours


class Instructor(ndb.Model):
  name = ndb.StringProperty()
  cert = ndb.StringProperty()
  expiration = ndb.DateProperty()
  signature = ndb.JsonProperty()


class Endorsement(ndb.Model):
  date = ndb.DateProperty()
  details = ndb.TextProperty()
  instructor = ndb.KeyProperty(kind=Instructor)


class Pilot(ndb.Model):
  user = ndb.UserProperty()
  name = ndb.StringProperty()
  birthday = ndb.DateProperty()
  photo = ndb.BlobKeyProperty()
  photo_url = ndb.TextProperty()
  cert_type = ndb.StringProperty()
  cert_num = ndb.StringProperty()
  instrument = ndb.BooleanProperty()
  medical_type = ndb.IntegerProperty(choices=(1, 2, 3))
  endorsements = ndb.KeyProperty(kind=Endorsement, repeated=True)
  bfr = ndb.KeyProperty(kind=Endorsement)
  last_medical = ndb.DateProperty()
  last_cessna_flight = ndb.DateTimeProperty()
  last_night_flight = ndb.DateTimeProperty()
  last_tailwheel_flight = ndb.DateTimeProperty()
  stats = ndb.KeyProperty(kind=FlightStats)


class Aircraft(ndb.Model):
  aircraft_class = ndb.StringProperty(choices=AIRCRAFT_CLASSES)
  category = ndb.StringProperty(choices=AIRCRAFT_CATEGORIES)
  engine_type = ndb.StringProperty(choices=ENGINE_TYPES)
  glass_panel = ndb.BooleanProperty()
  make = ndb.StringProperty()
  model = ndb.StringProperty()
  num_engines = ndb.IntegerProperty()
  photos = ndb.BlobKeyProperty(repeated=True)
  registration = ndb.StringProperty()
  tailwheel = ndb.BooleanProperty()
  retractable_gear = ndb.BooleanProperty()
  controllable_prop = ndb.BooleanProperty()
  has_flaps = ndb.BooleanProperty(default=True)
  high_performance = ndb.BooleanProperty()
  is_complex = ndb.ComputedProperty(
      lambda self: bool(self.retractable_gear
                        and self.controllable_prop
                        and self.has_flaps))

  @classmethod
  def QueryByPilot(cls, pilot):
    return cls.query(ancestor=pilot).order(cls.registration)

  def MakeAndModel(self):
    return '%s %s' % (self.make, self.model)


class Runway(ndb.Model):
  number = ndb.StringProperty()
  surface = ndb.StringProperty(choices=RUNWAY_TYPES)
  length = ndb.IntegerProperty()
  width = ndb.IntegerProperty()


class Airport(ndb.Model):
  name = ndb.StringProperty()
  runways = ndb.StructuredProperty(Runway, repeated=True)
  location = ndb.StringProperty()
  latlong = ndb.GeoPtProperty()


class Landing(ndb.Model):
  airport = ndb.KeyProperty(kind=Airport)
  runway = ndb.StringProperty()
  night = ndb.BooleanProperty(default=False)

  @classmethod
  def LandingsByFlight(cls, flight):
    return cls.query(ancestor=flight.key).order(-cls.airport)


class Weather(ndb.Model):
  airport = ndb.KeyProperty(kind=Airport)
  metar = ndb.StringProperty()
  taf = ndb.StringProperty()


class Telemetry(ndb.Model):
  type = ndb.StringProperty()
  data = ndb.BlobKeyProperty(repeated=True)


class Approach(ndb.Model):
  airport = ndb.KeyProperty(kind=Airport)
  runway = ndb.IntegerProperty()
  navaid = ndb.StringProperty(choices=NAVAIDS)
  missed = ndb.BooleanProperty(default=False)


class Flight(ndb.Model):
  pilot = ndb.KeyProperty(kind=Pilot)
  date = ndb.DateTimeProperty()
  aircraft = ndb.KeyProperty(kind=Aircraft)

  start_airport = ndb.KeyProperty(kind=Airport)
  intermediate_airports = ndb.KeyProperty(kind=Airport, repeated=True)
  filed_route = ndb.TextProperty()
  end_airport = ndb.KeyProperty(kind=Airport)
  approaches = ndb.TextProperty(repeated=True)
  landings = ndb.KeyProperty(kind=Landing, repeated=True)

  instructor = ndb.KeyProperty(kind=Instructor)
  passengers = ndb.StringProperty(repeated=True)

  pic_hours = ndb.FloatProperty(default=0)
  xc_hours = ndb.FloatProperty(default=0)
  dual_hours = ndb.FloatProperty(default=0)
  imc_hours = ndb.FloatProperty(default=0)
  sim_imc_hours = ndb.FloatProperty(default=0)
  simulator_hours = ndb.FloatProperty(default=0)
  ground_instruction_hours = ndb.FloatProperty(default=0)
  night_hours = ndb.FloatProperty(default=0)
  duration = ndb.ComputedProperty(
      lambda self: max(self.pic_hours, self.dual_hours))

  photos = ndb.BlobKeyProperty(repeated=True)
  photo_urls = ndb.TextProperty(repeated=True)
  telemetry = ndb.KeyProperty(kind=Telemetry, repeated=True)

  remarks = ndb.TextProperty()
  cost = ndb.FloatProperty(default=0)

  @classmethod
  def QueryByPilot(cls, pilot, reverse=False):
    if reverse:
      return cls.query(cls.pilot == pilot).order(cls.date)
    else:
      return cls.query(cls.pilot == pilot).order(-cls.date)


class Trip(ndb.Model):
  flights = ndb.KeyProperty(kind=Flight, repeated=True)
  comments = ndb.TextProperty()
  photos = ndb.BlobKeyProperty(repeated=True)
